"""
媒体录音机组件。
"""
from typing import ClassVar, Callable, Type, Any, Optional

from ..base import BaseSession, BaseSessionType, BaseSessionParentType
from ..framework import dispatch, session_received
from ..utils import to_any, callback, from_any, FloatValue, StringValue


@session_received
class MediaRecorder(BaseSession):
    """
    媒体录音机
    使用 create 方法创建录音机对象实例
    调用示例：
            recorder = await MediaRecorder.create(activity, '3gp')
            await recorder.start_record()
            await recorder.stop()
            recorder.upload(url)
    """
    name: ClassVar[str] = 'MediaRecorder'

    @classmethod
    async def create(cls: Type[BaseSessionType], parent: BaseSessionParentType,
                     *args: Any, **kwargs: dict) -> BaseSessionType:
        """
        format:音频格式（可选值： 3gp ogg aac）
        """
        if len(args) < 1:
            raise TypeError('Missing 1 required positional argument: `format`.')
        format_list = ['3gp', 'ogg', 'aac']
        if not args[0] in format_list:
            raise ValueError(f'The `{args[0]}` is unsupported with the media recorder.'
                             f' Valid format is in {format_list}')
        _format = to_any(args[0], StringValue)
        return await super().create(parent, _format, *args[1:], **kwargs)

    async def on_receive(self, msg):
        """
        组件的事件转发。
        """
        coro = self.on_upload(*msg.argv, session_id=msg.session, future_id=msg.future) if msg.name == 'onUpload' else \
            self.on_stop(*msg.argv, session_id=msg.session, future_id=msg.future) if msg.name == 'onStop' else \
            self.on_get_filename(
                *msg.argv, session_id=msg.session, future_id=msg.future
            ) if msg.name == 'onGetFilename' else \
            super().on_receive(msg)
        dispatch(coro)

    async def start(self):
        """
        启动客户端录音。
        """
        dispatch(self.send_action('start'))

    async def stop(self):
        """
        停止录音。
        """
        return await self.send_action_until_return('stop')

    async def upload(self, url: str, func: Optional[Callable] = None):
        """
        开始上传录音。
        :param url: 接收的url，需实现web标准的文件上传接口。
        :param func: 监听进度事件的异步函数（函数需实现一个参数progress: float），可以为None，如果是None则不接收上传进度。
        """
        if not func:
            return await self.send_action_until_return('upload', to_any(url, StringValue), to_any(None))

        async def inner(progress: Any):
            """
            内部使用。
            """
            await func(from_any(progress, FloatValue))

        return await self.send_action_until_return('upload', to_any(url, StringValue), self.create_callback(inner))

    @callback(bool)
    async def on_upload(self, result, session_id=0, future_id=0):
        """
        当录音机完成上传录音后，此方法被自动调用。
        通常不需要关注此方法的实现。
        """
        if not session_id:
            return
        self.set_action_result(session_id, future_id, result)

    @callback(bool)
    async def on_stop(self, result, session_id=0, future_id=0):
        """
        当录音机停止录制后，此方法被自动调用。
        通常不需要关注此方法的实现。
        """
        if not session_id:
            return
        self.set_action_result(session_id, future_id, result)

    async def get_filename(self) -> Optional[str]:
        """
        获取文件名称。
        使用示例：
        print(await uploader.get_filename())
        """
        return await self.send_action_until_return('getFilename')

    @callback(str)
    async def on_get_filename(self, filename, session_id=0, future_id=0):
        """
        当获取上传器文件名成功后，此方法被自动调用。
        通常不需要关注此方法的实现。
        """
        if not session_id:
            return
        self.set_action_result(session_id, future_id, filename)

    async def pause(self):
        """
        暂停录音。
        """
        dispatch(self.send_action("pause"))

    async def resume(self):
        """
        继续录音。
        """
        dispatch(self.send_action('resume'))
